package com.sxkiler.demo.seata.datasource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import io.seata.rm.datasource.xa.DataSourceProxyXA;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@ConditionalOnClass({DataSourceProperties.class})
@EnableConfigurationProperties({DataSourceProperties.class})
public class DynamicDataSourceConfiguration {
    @Value("${seata.resourceGroupId:DEFAULT}")
    String seataResourceGroupId;
    @Value("${seata.enabled:false}")
    boolean enableSeata;
    @Value("${seata.data-source-proxy-mode:}")
    String seataProxyMode;

    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    @Bean
    public HikariConfig hikariConfig(DataSourceProperties dataSourceProperties){
        HikariConfig config = new HikariConfig();
        config.setDriverClassName(dataSourceProperties.getDriverClassName());
        config.setJdbcUrl(dataSourceProperties.getUrl());
        config.setUsername(dataSourceProperties.getUsername());
        config.setPassword(dataSourceProperties.getPassword());
        return config;
    }

    @Bean
    public DataSource originDataSource(HikariConfig hikariConfig){
        HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
        return hikariDataSource;
    }
    @Bean(DynamicDataSource.PRIMARY_DATA_SOURCE)
    @Primary
    public DataSource dataSource(@Qualifier(DynamicDataSource.ORIGIN_DATA_SOURCE) DataSource originDataSource
    ,HikariConfig hikariConfig){
        DataSource primaryDataSource = originDataSource;
        DataSource proxyDataSource = null;
        if(enableSeata){
            HikariDataSource seataDataSource = new HikariDataSource(hikariConfig);
            if("XA".equals(seataProxyMode)){
                proxyDataSource = new DataSourceProxyXA(seataDataSource,seataResourceGroupId);
            }else if("AT".equals(seataProxyMode)){
                proxyDataSource = new DataSourceProxy(seataDataSource,seataResourceGroupId);
            }
        }
        //构造动态数据源
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object,Object> targetDataSources = new HashMap<>();
        if(proxyDataSource!=null){
            targetDataSources.put(DynamicDataSource.PROXY_DATA_SOURCE,proxyDataSource);
            DynamicDataSourceHolder.addDataSourceId(DynamicDataSource.PROXY_DATA_SOURCE);
            primaryDataSource = proxyDataSource;
        }
        targetDataSources.put(DynamicDataSource.ORIGIN_DATA_SOURCE,originDataSource);
        DynamicDataSourceHolder.addDataSourceId(DynamicDataSource.ORIGIN_DATA_SOURCE);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        return dynamicDataSource;
    }

    @Bean("transactionManager")
    @Primary
    public PlatformTransactionManager transactionManager(
            @Qualifier(DynamicDataSource.PRIMARY_DATA_SOURCE)DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean("transactionTemplate")
    @Primary
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager){
        return new TransactionTemplate(transactionManager);
    }
}
